#pragma once
#include "GL/glad.h"
#include <vector>
#include <GLM/glm.hpp>
#include <memory>
#include <array>
#include "common.h"

#define SAFE_DELETE(p) if(p){ delete p; p = nullptr; }

GLint  createVAO(const GLvoid* vVertices, GLint vVerticesSize, std::initializer_list<GLint> vAttribsLength, const GLint vIndices[] = nullptr, GLint vIndicesSize = 0, int *voVBO = nullptr);
GLint  createVAO(int vBufferIndex, std::initializer_list<GLint> vAttribsLength, GLenum vDataType = GL_FLOAT);
GLint  genBuffer(GLenum vTarget, GLsizeiptr vSize, const GLvoid *vData, GLenum vUsage, GLint vBindingIndex = -1);
GLvoid genTexture(std::shared_ptr<Graphics::STexture> vioTexture/* = ElayGraphics::STexture()*/);
// GLvoid loadTextureFromFile(const std::string& vFilePath);
GLvoid loadTextureFromFile(const std::string& vFilePath, std::shared_ptr<Graphics::STexture> voTexture2D);
GLint  createVAO4ScreenQuad();
GLint  createVAO4Cube();
GLint  createVAO4Sphere();
GLint  createVAO4VoxelCubes(std::vector<glm::vec3>& vMinVerts, GLfloat vLength);
void   drawQuad();
void   drawCube(bool vIsPatch = false);
void   drawSphere();
void   drawVoxelCubes(std::vector<glm::vec3>& vMinVerts, GLfloat vLength);
GLint  genFBO(const std::initializer_list<std::shared_ptr<Graphics::STexture>>& vioTextureAttachments);
void   transferData2Buffer(GLenum vTarget, GLint vTargetID, std::vector<GLintptr> vOffsets, std::vector<GLsizeiptr> vSizes, std::vector<const GLvoid*> vDatas);
int    captureScreen2Img(const std::string& vFileName, int vQuality = 100.0);
void   hueToRGB(float vHue, glm::vec4& voRGB);
void   blitFrameBufferTexture(std::shared_ptr<Graphics::STexture> vioSrcTexture, std::shared_ptr<Graphics::STexture> vioDestTexture, GLenum vFilter = GL_NEAREST, std::array<bool, 4> vChannels = { true, true, true, true});
GLint  createInstanceID();

bool floatEqual(float vFloatNum1, float vFloatNum2);

template<class T>
void bindInstanceArray(GLint vVAO, GLint vInstanceID, GLint vLocation, const std::vector<T>& vInstanceArray, int vLength, int vStride, int vOffset)
{
	glBindVertexArray(vVAO);
	glBindBuffer(GL_ARRAY_BUFFER, vInstanceID);
	glBufferData(GL_ARRAY_BUFFER, vInstanceArray.size() * sizeof(T), &vInstanceArray[0], GL_STATIC_DRAW);

	glEnableVertexAttribArray(vLocation);
	glVertexAttribPointer(vLocation, vLength, GL_FLOAT, GL_FALSE, vStride * sizeof(float), (void*)(vOffset * sizeof(float)));
	glVertexAttribDivisor(vLocation, 1);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);
}

template<class T>
void bindInstanceArray(GLint vVAO, GLint vInstanceID, GLint vLocation, T* vInstanceArray, int vSize, int vLength, int vStride, int vOffset)
{
	glBindVertexArray(vVAO);
	glBindBuffer(GL_ARRAY_BUFFER, vInstanceID);
	glBufferData(GL_ARRAY_BUFFER, vSize * sizeof(T), vInstanceArray, GL_STATIC_DRAW);

	glEnableVertexAttribArray(vLocation);
	glVertexAttribPointer(vLocation, vLength, GL_FLOAT, GL_FALSE, vStride * sizeof(float), (void*)(vOffset * sizeof(float)));
	glVertexAttribDivisor(vLocation, 1);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);
}